package com.ubiquando.api.server.db;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.Query;

import org.mortbay.log.Log;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;

public enum Dao {
	INSTANCE;

	@SuppressWarnings("unchecked")
	public Client addClient(String name, String email, String pwd,
			String location, String regID/*, String GToken*/) {
		synchronized (this) {
			
			EntityManager em = EMF.get().createEntityManager();
			Query q = em.createQuery("select c from Client c where c.email = '"+email+"'");
			List<Client> result = q.getResultList();
			if(result.size() != 0)
				return null;
			
			Client newClient = new Client();
			newClient.setName(name);
			newClient.setEmail(email);
			newClient.setPwd(pwd);
			newClient.setLocation(location);
			newClient.setRegistrationID(regID);
			//			newClient.setClientLoginToken(GToken);
			
			em.getTransaction().begin();
			try {
				em.persist(newClient);
				em.getTransaction().commit();
			} finally {
				if (em.getTransaction().isActive())
					em.getTransaction().rollback();
			}
			newClient = em.merge(newClient);
			return newClient;
		}
	}

	public Client connect(String email, String pwd, boolean connection) {
		synchronized (this) {
			boolean result = false;
			Client client = null;
			EntityManager em = EMF.get().createEntityManager();
			// Key id = KeyFactory.stringToKey(clientId);
			// Client client = em.find(Client.class, id);//(Client)
			// q.getSingleResult();
			Query q = em.createQuery("select c from Client c where c.email = '"
					+ email + "' AND c.pwd = '" + pwd + "'");
			try {
				client = (Client) q.getSingleResult();
				try {
					em.getTransaction().begin();
					client.setConnected(connection);
					em.merge(client);
					em.getTransaction().commit();
				} finally {
					if (em.getTransaction().isActive())
						em.getTransaction().rollback();
				}
				result = true;
			} catch (NoResultException e) {
				return null;
			}
//			System.out.println("the result is " + result);
			return em.merge(client);
		}
	}

	public Event addMeetingEvent(int type, String name, String creatorId,
			long time, String dest/*, String destName*/) {
		synchronized (this) {
			EventMeeting newEvent = new EventMeeting();// type, name, creator,
			// time, dest);
			newEvent.setType(type);
			newEvent.setName(name);
			newEvent.setCreatorId(creatorId);
			newEvent.setTime(time);
			newEvent.setDest(dest);
			newEvent.addParticipant(creatorId);
			//			newEvent.setDestName(destName);

			EntityManager em = EMF.get().createEntityManager();
			em.getTransaction().begin();
			try {
				em.persist(newEvent);
				em.getTransaction().commit();
			} finally{
				if(em.getTransaction().isActive()){
					em.getTransaction().rollback();
					return null;
				}
			}
			newEvent = em.merge(newEvent);
			em.close();
			
			em = EMF.get().createEntityManager();
			Client creator = em.find(Client.class, KeyFactory.stringToKey(creatorId));
			if(creator != null){
				em.getTransaction().begin();
				try{
					creator.addEvent(KeyFactory.keyToString(newEvent.getId()), type);
					em.merge(creator);
					em.getTransaction().commit();
				}finally{
					if(em.getTransaction().isActive())
						em.getTransaction().rollback();
				}
			}
		
			return newEvent;
		}
	}

	public Event addPickupEvent(int type, String name, String creatorId,
			long time, String dest, /*String destName,*/ String location, String pickerId) {
		synchronized (this) {
			EventPickUp newEvent = new EventPickUp();// type, name, creator,
			// time, dest, location,
			// picker);
			newEvent.setType(type);
			newEvent.setName(name);
			newEvent.setCreatorId(creatorId);
			newEvent.setTime(time);
			newEvent.setDest(dest);
			//			newEvent.setDestName(destName);
			newEvent.setLocation(location);
			newEvent.setPickerId(pickerId);

			EntityManager em = EMF.get().createEntityManager();
			em.getTransaction().begin();
			try {
				em.persist(newEvent);
				em.getTransaction().commit();
			} catch (Exception e) {
				// TODO what todo?
			}
			newEvent = em.merge(newEvent);
			em.close();
			
			em = EMF.get().createEntityManager();
			Client creator = em.find(Client.class, KeyFactory.stringToKey(creatorId));
			if(creator != null){
				em.getTransaction().begin();
				try{
					creator.addEvent(KeyFactory.keyToString(newEvent.getId()), type);
					em.merge(creator);
					em.getTransaction().commit();
				}finally{
					if(em.getTransaction().isActive())
						em.getTransaction().rollback();
				}
			}
			return newEvent;
		}
	}

	public Client getClient(String clientId) {
		synchronized(this){
			EntityManager em = EMF.get().createEntityManager();
			// Query q = em.createQuery("select from Client where id="+clientId);
			Key id = KeyFactory.stringToKey(clientId);
			Client client = em.find(Client.class, id);
			// Client client = (Client) q.getSingleResult();
			// em.close();
			return client;
		}
	}

	public PickupClient getPickupClient(String clientId) {
		synchronized(this){
			EntityManager em = EMF.get().createEntityManager();
			Key id = KeyFactory.stringToKey(clientId);
			PickupClient client = em.find(PickupClient.class, id);
			return client;
		}
	}

	public Event getEvent(String eventId, int eventType) {
		synchronized(this){
			if (eventType == Event.MEETING_EVENT)
				return getMeetingEvent(eventId);
			else if (eventType == Event.PICKUP_EVENT)
				return getPickupEvent(eventId);
			return null;
		}
	}

	private Event getPickupEvent(String eventId) {
		EntityManager em = EMF.get().createEntityManager();
		Key id = KeyFactory.stringToKey(eventId);
		Event event = em.find(EventPickUp.class, id);
		return event;
	}

	private Event getMeetingEvent(String eventId) {
		EntityManager em = EMF.get().createEntityManager();
		Key id = KeyFactory.stringToKey(eventId);
		Event event = em.find(EventMeeting.class, id);

//		System.out.println("participants");
		for(String p:event.getParticipants())
			System.out.println(p);

		return event;
	}

	public Event meetingParticipation(String clientId, String eventId,
			int eventType) {
		synchronized(this){
			System.out.println("in meetingParticipation");
			EntityManager em = EMF.get().createEntityManager();
			Event event = em.find(EventMeeting.class, KeyFactory.stringToKey(eventId));
			if (event == null)
				return null;
			try {
				System.out.println("found event. event beginning transaction.");
				em.getTransaction().begin();
				event.addParticipant(clientId);
				em.merge(event);
				em.getTransaction().commit();
				System.out.println("comitted");
			} finally {
				if (em.getTransaction().isActive()){
					em.getTransaction().rollback();
					return null;
				}
			}
			em.close();
			System.out.println("em closed");
			
			em = EMF.get().createEntityManager();
			Client client = em.find(Client.class, KeyFactory.stringToKey(clientId));
			if(client == null)
				return null;
			try{
				System.out.println("found client. client beginning transaction.");
				em.getTransaction().begin();
				client.addEvent(eventId, eventType);
				em.merge(client);
				em.getTransaction().commit();
				System.out.println("comitted");
			} finally {
				if (em.getTransaction().isActive()){
					em.getTransaction().rollback();
					return null;
				}
			}
			System.out.println("em closed");
			
			return em.merge(event);
		}
	}

	public Event pickupParticipation(String clientId, String eventId,
			int eventType, String location, String locName, long time) {
		synchronized(this){
			EntityManager em = EMF.get().createEntityManager();

			PickupClient pClient = new PickupClient();
			pClient.setClientId(clientId);
			pClient.setEventId(eventId);
			pClient.setLocation(location);
			pClient.setLocName(locName);
			pClient.setTime(time);
			try {
				em.getTransaction().begin();
				em.persist(pClient);
				em.getTransaction().commit();
			} finally {
				if (em.getTransaction().isActive()){
					em.getTransaction().rollback();
					return null;
				}
			}
			em.close();
			
			em = EMF.get().createEntityManager();
			Client client = em.find(Client.class, KeyFactory.stringToKey(clientId));
			if (client == null)
				return null;
			try {
				em.getTransaction().begin();
				client.addEvent(eventId, eventType);
				em.merge(client);
				em.getTransaction().commit();
			} finally {
				if (em.getTransaction().isActive()){
					em.getTransaction().rollback();
					return null;
				}
			}
			em.close();
			
			em = EMF.get().createEntityManager();
			Event event = em.find(EventPickUp.class,
					KeyFactory.stringToKey(eventId));
			if (event == null)
				return null;
			try{
				em.getTransaction().begin();
				event.addParticipant(KeyFactory.keyToString(pClient.getId()));
				em.merge(event);
				em.getTransaction().commit();
				
			}finally {
				if (em.getTransaction().isActive()){
					em.getTransaction().rollback();
					return null;
				}
			}
			return em.merge(event);
		}
	}


	public Client updateClientLocation(String clientId, String newLocation) {
		synchronized (this) {
			EntityManager em = EMF.get().createEntityManager();
			Key id = KeyFactory.stringToKey(clientId);
			Client client = em.find(Client.class, id);
			try {
				em.getTransaction().begin();
				client.setLocation(newLocation);
				em.merge(client);
				em.getTransaction().commit();
			} finally {
				if (em.getTransaction().isActive())
					em.getTransaction().rollback();
			}
			return em.merge(client);
		}
	}

	public Event startEvent(String eventId, int eventType) {
		synchronized(this){
			EntityManager em = EMF.get().createEntityManager();
			Key id = KeyFactory.stringToKey(eventId);
			Event event = null;
			if (eventType == Event.MEETING_EVENT)
				event = em.find(EventMeeting.class, id);
			else if (eventType == Event.PICKUP_EVENT)
				event = em.find(EventPickUp.class, id);
			try {
				em.getTransaction().begin();
				event.setStarted(true);
				em.merge(event);
				em.getTransaction().commit();
			} finally {
				if (em.getTransaction().isActive())
					em.getTransaction().rollback();
			}
			return em.merge(event);
		}
	}

	public Event removeParticipation(String clientId, String eventId,
			int eventType) {
		synchronized(this){
			if (eventType == Event.MEETING_EVENT)
				return removeFromMeeting(clientId, eventId);
			else if (eventType == Event.PICKUP_EVENT)
				return removeFromPickup(clientId, eventId);
			else
				return null;
		}
	}

	private Event removeFromPickup(String clientId, String eventId) {
		EntityManager em = EMF.get().createEntityManager();
		Client client = em.find(Client.class, KeyFactory.stringToKey(clientId));
		if(client == null)
			return null;
		Query q = em.createQuery("select p from PickupClient p where p.clientId = '"
				+ clientId + "' AND p.eventId = '"+eventId+"'");
		PickupClient pClient = (PickupClient) q.getSingleResult();
		if(pClient == null)
			return null;
		try {
			em.getTransaction().begin();
			client.removeEvent(eventId, Event.PICKUP_EVENT);
			em.merge(client);
			em.getTransaction().commit();
		} finally {
			if (em.getTransaction().isActive()){
				em.getTransaction().rollback();
				return null;
			}
		}
		em.close();
		

		em = EMF.get().createEntityManager();
		Event event = em.find(EventPickUp.class, KeyFactory.stringToKey(eventId));
		if(event == null)
			return null;
		try{
			em.getTransaction().begin();
			event.removeParticipant(KeyFactory.keyToString(pClient.getId()));
			em.merge(event);
			em.getTransaction().commit();
			
		}finally {
			if (em.getTransaction().isActive()){
				em.getTransaction().rollback();
				return null;
			}
		}
		return em.merge(event);
	}

	private Event removeFromMeeting(String clientId, String eventId) {
		EntityManager em = EMF.get().createEntityManager();
		Client client = em.find(Client.class, KeyFactory.stringToKey(clientId));
		if(client == null)
			return null;
		try {
			em.getTransaction().begin();
			client.removeEvent(eventId, Event.MEETING_EVENT);
			em.merge(client);
			em.getTransaction().commit();
		} finally {
			if (em.getTransaction().isActive())
				em.getTransaction().rollback();
		}
		em.close();

		em = EMF.get().createEntityManager();
		Event event = em.find(EventMeeting.class, KeyFactory.stringToKey(eventId));
		if(event == null)
			return null;
		try {
			em.getTransaction().begin();
			event.removeParticipant(clientId);
			em.merge(event);
			em.getTransaction().commit();
		} finally {
			if (em.getTransaction().isActive())
				em.getTransaction().rollback();
		}
		return em.merge(event);
	}

	public Client addContact(String clientId1, String clientId2) {
		synchronized(this){
//			Log.info("in addContact");
			EntityManager em = EMF.get().createEntityManager();
			Client client1 = em.find(Client.class, KeyFactory.stringToKey(clientId1));
			if(client1 == null)
				return null;
			try{
				em.getTransaction().begin();
				client1.addContact(clientId2);
				em.merge(client1);
				em.getTransaction().commit();
			}finally{
				if(em.getTransaction().isActive()){
					em.getTransaction().rollback();
					return null;
				}
			}
			em.close();
//			Log.info("client1 added client2 as a contact");
			
			em = EMF.get().createEntityManager();
			Client client2 = em.find(Client.class, KeyFactory.stringToKey(clientId2));
			if(client2 == null)
				return null;
			try{
				em.getTransaction().begin();
				client2.addContact(clientId1);
				em.merge(client2);
				em.getTransaction().commit();
			}finally{
				if(em.getTransaction().isActive())
					em.getTransaction().rollback();
			}
//			Log.info("client2 added client1 as a contact");
			return em.merge(client2);
		}
	}

	public Event addPick2Meet(String meetingId, String pickupId) {
		synchronized(this){
			EntityManager em = EMF.get().createEntityManager();
			EventMeeting meetingEvent = em.find(EventMeeting.class, KeyFactory.stringToKey(meetingId));
			em.getTransaction().begin();
			try{
				meetingEvent.addPickupEvent(pickupId);
				meetingEvent = em.merge(meetingEvent);
				em.getTransaction().commit();
			}finally{
				if(em.getTransaction().isActive())
					em.getTransaction().rollback();
			}
			return meetingEvent;
		}
	}

	@SuppressWarnings("unchecked")
	public Client findFriend(String friendEmail) {
		synchronized(this){
			EntityManager em = EMF.get().createEntityManager();
			Query q = em.createQuery("select c from Client c where c.email = '"+friendEmail+"'");
			List<Client> friends =  q.getResultList();
			if(friends.size() != 1)
				return null;
			return friends.get(0);
		}
	}

	@SuppressWarnings({"rawtypes" })
	public boolean setClientLogin(String clientLogin) {
		synchronized(this){
			EntityManager em = EMF.get().createEntityManager();
			Query q = em.createQuery("select c from ClientLogin c");
			List logins = q.getResultList();
			if(logins.isEmpty()){
				ClientLogin auth = new ClientLogin();
				auth.setClientLogin(clientLogin);
				try{
					em.getTransaction().begin();
					em.persist(auth);
					em.getTransaction().commit();
				}finally{
					if(em.getTransaction().isActive()){
						em.getTransaction().rollback();
						return false;
					}
				}
				return true;
			}else if(logins.size() > 1){
				return false;
			}else{
				try{
					em.getTransaction().begin();
					((ClientLogin)logins.get(0)).setClientLogin(clientLogin);
					em.merge(logins.get(0));
					em.getTransaction().commit();
				}finally{
					if(em.getTransaction().isActive()){
						em.getTransaction().rollback();
						return false;
					}
				}
			}
			return false;
		}
	}

	@SuppressWarnings("unchecked")
	public String getClientLogin() {
		synchronized(this){
			EntityManager em = EMF.get().createEntityManager();
			Query q = em.createQuery("select c from ClientLogin c");
			List<ClientLogin> list = q.getResultList();
			if(list.size() == 1)
				return list.get(0).getClientLogin();
			else
				return "";
		}
	}

	public Event addPicker(String clientId, /*, String client2Id,*/ String eventId) {
		synchronized(this){
			EntityManager em = EMF.get().createEntityManager();
			EventPickUp event = em.find(EventPickUp.class, KeyFactory.stringToKey(eventId));
			em.getTransaction().begin();
			try{
				event.setPickerId(clientId);
				event.addParticipant(clientId);
				event = em.merge(event);
				em.getTransaction().commit();
			}finally{
				if(em.getTransaction().isActive()){
					em.getTransaction().rollback();
					return null;
				}
			}
			return event;
		}

	}
}